/**
 * WMGeo - A Geocaching PDA application
 * (c) Michal Kuchta 2008-2009
 */

#include "..\stdafx.h"
#include "sqlite.h"
#include <afxpriv.h>

/**
 * Constructor.
 */
SQLite::SQLite() {
	m_pDatabase = NULL;
	sqlite3_initialize();
} // SQLite::SQLite()

/**
 * Destructor.
 */
SQLite::~SQLite() {
	this->Close();
	sqlite3_shutdown();
} // SQLite::~SQLite()

/**
 * Open SQLite database specified by dbFile parameter
 * @param dbFile Database file to be opened
 */
int SQLite::Open(CString dbFile) {
	if (this->m_pDatabase) {
		this->Close();
	}
	
	this->m_sDbFile = dbFile;
	return sqlite3_open16(this->m_sDbFile, &(this->m_pDatabase));
} // SQLite::Open()

/**
 * Close currently opened SQLite database.
 */
int SQLite::Close() {
	if (this->m_pDatabase) {
		// Close all statements
		sqlite3_stmt *pStmt;
		while ((pStmt = sqlite3_next_stmt(this->m_pDatabase, 0)) != NULL) {
			sqlite3_finalize(pStmt);
		}
	
		int result = sqlite3_close(this->m_pDatabase);
		this->m_pDatabase = NULL;
		return result;
	} else {
		return 0;
	}
} // SQLite::Close()

/**
 * Prepare SQL statement to the database
 * @param query SQL query string
 * @return Prepared SQL statement
 */
SQLStatement SQLite::Prepare(CString query) {
	void *dummy;
	sqlite3_stmt *statement;
	int result = sqlite3_prepare16_v2(
		this->m_pDatabase,
		(const void *)(query.GetBuffer()),
		-1,
		&statement,
		(const void **)&dummy
	);
	if (result == SQLITE_OK) {
		return SQLStatement(this, statement);
	} else {
		Error e(this);
		throw e;
	}
} // SQLite::Prepare()

/**
 * Execute one SQL statement and return the result code.
 * @param query SQL query string
 * @return Result of the sqlite3_exec() call.
 */
int SQLite::Execute(CString query) {
	USES_CONVERSION;
	char *cquery = T2A(query.GetBuffer());
	return sqlite3_exec(
		this->m_pDatabase,
		cquery,
		NULL,
		NULL,
		&(this->m_pLastError)
	);
} // SQLite::Execute()

/**
 * Get ID of last row inserted using INSERT operation.
 */
int SQLite::LastInsertId() {
	return (int)sqlite3_last_insert_rowid(this->m_pDatabase);
} // SQLite::LastInsertId()

/**
 * Get last error message
 */
const wchar_t *SQLite::GetLastError() {
	return (const wchar_t *)sqlite3_errmsg16(this->m_pDatabase);
} // SQLite::GetLastError()

/**
 * Default constructor
 * @param sqlite SQLite database connection
 * @param statement SQLite prepared statement
 */
SQLStatement::SQLStatement(SQLite *sqlite, sqlite3_stmt *statement):
	m_pSQLite(sqlite), m_pStatement(statement) {

	this->m_iInstanceCount = (int *)malloc(sizeof(int));
	*(this->m_iInstanceCount) = 1;
} // SQLStatement::SQLStatement()

/**
 * Copy constructor - creates only new reference to existing data
 */
SQLStatement::SQLStatement(SQLStatement const &sqlstatement):
	m_pSQLite(sqlstatement.m_pSQLite), m_pStatement(sqlstatement.m_pStatement),
	m_iInstanceCount(sqlstatement.m_iInstanceCount) {
	
	// Increase instance count
	++(*(this->m_iInstanceCount));
} // SQLStatement::SQLStatement()

/**
 * Destructor.
 */
SQLStatement::~SQLStatement() {
	if (--(*(this->m_iInstanceCount)) == 0) {
		sqlite3_finalize(this->m_pStatement);
		free(this->m_iInstanceCount);
	}
} // SQLStatement::~SQLStatement()

/**
 * Bind blob to value.
 * @param index Value index
 * @param value Value data
 * @param length Data length
 */
void SQLStatement::Bind(int index, const void *value, int length) {
	sqlite3_bind_blob(this->m_pStatement, index, value, length, SQLITE_TRANSIENT);
} // SQLStatement::Bind()

/**
 * Bind double to value
 * @param index Value index
 * @param value Value
 */
void SQLStatement::Bind(int index, double value) {
	sqlite3_bind_double(this->m_pStatement, index, value);
} // SQLStatement::Bind()

/**
 * Bind int to value
 * @param index Value index
 * @param value Value
 */
void SQLStatement::Bind(int index, int value) {
	sqlite3_bind_int(this->m_pStatement, index, value);
} // SQLStatement::Bind()

/**
 * Bind 64bit int to value
 * @param index Value index
 * @param value Value
 */
void SQLStatement::Bind(int index, sqlite3_int64 value) {
	sqlite3_bind_int64(this->m_pStatement, index, value);
} // SQLStatement::Bind()

/**
 * Bind NULL to value
 * @param index Value index
 */
void SQLStatement::Bind(int index) {
	sqlite3_bind_null(this->m_pStatement, index);
} // SQLStatement::Bind()

/**
 * Bind string to value
 * @param index Value index
 * @param value NULL-terminated string
 */
void SQLStatement::Bind(int index, const char *value) {
	sqlite3_bind_text(this->m_pStatement, index, value, strlen(value), SQLITE_TRANSIENT);
} // SQLStatement::Bind()

/**
 * Execute the statement
 * @return Result of sqlite3_step() call.
 */
int SQLStatement::Step() {
	return sqlite3_step(this->m_pStatement);
} // SQLStatement::Step()

/**
 * Get column value as BLOB.
 * @param index Column index
 * @param length [OUT] length of blob data
 * @return Pointer to data and data length in the length parameter.
 */
const void *SQLStatement::ColumnB(int index, int *length) {
	if (length != NULL) {
		*length = sqlite3_column_bytes(this->m_pStatement, index);
	}
	return sqlite3_column_blob(this->m_pStatement, index);
} // SQLStatement::ColumnB()

/**
 * Get column value as int.
 * @param index Column index
 * @return Column value as integer
 */
int SQLStatement::ColumnI(int index) {
	return sqlite3_column_int(this->m_pStatement, index);
} // SQLStatement::ColumnI()

/**
 * Get column value as double.
 * @param index Column index
 * @return Column value as double
 */
double SQLStatement::ColumnD(int index) {
	return sqlite3_column_double(this->m_pStatement, index);
} // SQLStatement::ColumnD()

/**
 * Get column value as int64
 * @param index Column index
 * @return Column value as int64
 */
sqlite3_int64 SQLStatement::ColumnI64(int index) {
	return sqlite3_column_int64(this->m_pStatement, index);
} // SQLStatement::ColumnI64()

/**
 * Get column value as string
 * @param index Column index
 * @return Column value as string
 */
const unsigned char *SQLStatement::ColumnT(int index) {
	return sqlite3_column_text(this->m_pStatement, index);
} // SQLStatement::ColumnT()
